import psutil, time, re, csv, fnmatch
from linux_analyser.utils.stoppable_thread import StoppableThread

class BasicGatherer(StoppableThread):
    "parent class for gatherers"
    
    def __init__(self, interval = 5, threshold = 0, username = "*", name = "*", filename = "../tmp/test.csv" , lock = None):
        "gatherer initialization"
        StoppableThread.__init__(self)
        self.interval = interval
        self.threshold = threshold
        self.procs = [p for p in psutil.process_iter()]  # the current process list
        # glob-like regexps
        self.username_regexp = re.compile(fnmatch.translate(username))
        self.name_regexp = re.compile(fnmatch.translate(name))
        self.to_write = []
        self.filename = filename
        self.lock = lock                

    def run(self):
        "gatherer main loop (running in thread)"
        while True:
            start_time = time.time()
            if self.stopped():
                print "Gatherer stopped (%s)." % self.__class__.__name__
                with self.lock:
                    f = open(self.filename, 'ab')
                    writer = csv.writer(f)
                    [writer.writerow(row) for row in self.to_write]
                    f.close()
                break
            self.gather()
            # for more precise interval ;)
            remaining_time = self.interval - (time.time() - start_time)
            if remaining_time > 0:
                time.sleep(remaining_time)
        
    def filter(self, process):
        "wrapper around method filtering processes"
        try:
            return self.filter_method(process)
        except psutil.NoSuchProcess:
            self.procs.remove(process)
            return False

    def gather(self):
        "main gathering method"
        self.log(self.summary_info_string())
        # self.log(["==================================================================="])
        # add new processes to procs list; processes which have gone
        # in meantime will be removed from the list later
        cpids = [p.pid for p in self.procs]
        for p in psutil.process_iter():
            if p.pid not in cpids:
                self.procs.append(p)
                
        process_info_list = [self.filter(p) for p in self.procs]
        output = [s for s in process_info_list if s]
        output = sorted(output, key=lambda s: s[-1], reverse=True)
        if len(output) > 0:
            for string in output:
                self.log(string)
            # self.log(["==================================================================="])
            
    def log(self, string_array):
        """function managing logging and/or printing the output"""
        # TODO: what options for logging: only file, file + stdout?
        timed_array = [self.formatted_local_time()]
        timed_array.extend(string_array)
        print ", ".join(timed_array)
        self.to_write.append(timed_array)
        #self.writer.writerow(timed_array)

    def formatted_local_time(self):
        "returns UTC datetime in IS0 8601 extended format"
        return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
            
    def filter_method(self, process):
        """function determining if process usage should be taken
        in consideration (matching preferences about threshold usage,
        name or other), returning process information string or False"""
        raise NotImplementedError, "Method should be implemented in subclass!"

    def summary_info_string(self):
        "function returning string of summary gathering info"
        raise NotImplementedError, "Method should be implemented in subclass!"        
        
class CPUGatherer(BasicGatherer):
    "gatherer for CPU"

    def summary_info_string(self):
        return ["CPU", "%.2f" % psutil.cpu_percent()]
    
    def filter_method(self, process):      
        cpu_usage = process.get_cpu_percent(None)
        if cpu_usage < self.threshold:
            return False
        username = process.username
        if not self.username_regexp.match(username):
            return False        
        name = process.name
        if not self.name_regexp.match(name):
            return False
        return ["CPU", name, username, "%.2f" % cpu_usage]
        
        
class RAMGatherer(BasicGatherer):
    "gatherer for RAM"
    
    def summary_info_string(self):        
        return ["RAM", "%.2f" % psutil.phymem_usage().percent]
        
    def filter_method(self, process):
        # ram usage in MB
        ram_usage = process.get_memory_percent()
        if ram_usage < self.threshold:
            return False
        username = process.username
        if not self.username_regexp.match(username):
            return False        
        name = process.name
        if not self.name_regexp.match(name):
            return False
        return ["RAM", name, username, "%.2f" % ram_usage]                    
